package com.duolebo.uteped_sdk.msg;

import android.Manifest;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;

import androidx.annotation.NonNull;

import com.duolebo.uteped_sdk.UTEPedEventHandler;
import com.duolebo.uteped_sdk.utils.Const;
import com.duolebo.uteped_sdk.utils.Logger;
import com.duolebo.uteped_sdk.utils.PermissionHelper;
import com.duolebo.uteped_sdk.utils.Tools;
import com.yc.nadalsdk.bean.Response;
import com.yc.nadalsdk.bean.TimeClock;
import com.yc.nadalsdk.bean.TimeDisplay;
import com.yc.nadalsdk.ble.open.UteBleClient;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;

/**
 * 与flutter通信回调基础类
 * 开发时间比较紧张，以业务划分，后期尽量细分
 */
public abstract class BaseMessage {
    protected final static int DATA_BOOL = 0;
    protected final static int DATA_STRING = 1;
    protected final static int DATA_MAP = 2;
    protected UTEPedEventHandler eventHandler;
    protected MethodChannel methodChannel;
    protected UteBleClient uteBleClient;
    protected Handler handler = new Handler(Objects.requireNonNull(Looper.myLooper()));

    /**
     * BaseCallback
     * @param methodChannel methodChannel
     */
    public BaseMessage(MethodChannel methodChannel, UTEPedEventHandler eventHandler) {
        this.methodChannel = methodChannel;
        this.eventHandler = eventHandler;
        this.uteBleClient = UteBleClient.getUteBleClient();
    }

    /**
     * methodCall 事件传递
     * @param call call
     * @param result result
     * @return true表示已经处理，false则继续传递
     */
    public boolean methodCallConsumed(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
        if (!call.hasArgument(Const.KEY_METHOD))
            return false;

        int dataType = getDataType(call.method);
        d("data type: " + dataType);

        boolean consumed = true;
        String method = call.argument(Const.KEY_METHOD);
        boolean success = false;
        assert method != null;
        switch (method) {
            case Const.M_checkPermissions:
                String permissionType = call.argument(Const.KEY_PERMISSION);
                assert permissionType != null;
                onCheckPermission(permissionType);
                result.success(true);
                break;

            case Const.M_queryPermissions:
                if (call.hasArgument(Const.KEY_LIST)) {
                    List<String> permissions = call.argument(Const.KEY_LIST);
                    assert permissions != null;
                    result.success(onQueryPermissions(permissions));
                }
                else {
                    result.success(false);
                }
                break;

            case Const.M_hasPermission:
                String permission = call.argument(Const.KEY_PERMISSION);
                assert permission != null;
                result.success(hasPermission(permission));
                break;

            case Const.M_toPermissionPage:
                String type = call.argument(Const.KEY_TYPE);
                if (Const.KEY_SYSTEM.equals(type)) {
                    permission = call.argument(Const.KEY_PERMISSION);
                    if (Const.KEY_NOTIFICATION.equals(permission)) {
                        Tools.openNotificationListenSettings(Tools.getApplicationContext());
                    }
                    else {
                        PermissionHelper.toMyAppPermissionSetting(Tools.getApplicationContext());
                    }
                }
                else {
                    PermissionHelper.toMyAppPermissionSetting(Tools.getApplicationContext());
                }
                break;

            case Const.M_log:
                int logType = Log.DEBUG;
                if (call.hasArgument(Const.KEY_TYPE)) {
                    logType = call.argument(Const.KEY_TYPE);
                }
                String tag = call.argument(Const.KEY_TAG);
                String msg = call.argument(Const.KEY_VALUE);
                if (Log.VERBOSE == logType) {
                    Log.v(tag, Objects.toString(msg));
                }
                else if (Log.INFO== logType) {
                    Log.i(tag, Objects.toString(msg));
                }
                else if (Log.DEBUG == logType) {
                    Log.d(tag, Objects.toString(msg));
                }
                else if (Log.WARN == logType) {
                    Log.w(tag, Objects.toString(msg));
                }
                else if (Log.ERROR == logType) {
                    Log.e(tag, Objects.toString(msg));
                }
                result.success(true);
                break;

            case Const.M_isBluetoothEnable:
                result.success(uteBleClient.isBluetoothEnable());
                break;

            case Const.M_isConnected:
                String address = "";
                if (call.hasArgument(Const.KEY_ADDRESS)) {
                    address = call.argument(Const.KEY_ADDRESS);
                }
                if (TextUtils.isEmpty(address)) {
                    result.success(uteBleClient.isConnected());
                }
                else {
                    result.success(uteBleClient.isConnected(address));
                }
                break;

            case Const.M_isConnectedGatt:
                result.success(uteBleClient.isConnectedGatt());
                break;

            case Const.M_isDeviceBusy:
                if (uteBleClient.isConnected()) {
                    success = uteBleClient.isDeviceBusy();
                }
                result.success(success);
                break;

            case Const.M_setTimeDisplay:
                if (uteBleClient.isConnected()) {
                    Response<?> rsp = uteBleClient.getUteBleConnection().setTimeDisplay(getTimeDisplay(call));
                    success = rsp.getErrorCode() == Const.CODE_SUCCESS;
                }
                result.success(success);

                break;

            case Const.M_checkGPS:
                if (!Tools.isGPSOpen()) {
                    Tools.openGPSSettings();
                }
                break;

            case Const.M_isGPSOpen:
                result.success(Tools.isGPSOpen());
                break;

            default:
                consumed = false;
        }
        return consumed;
    }

    /**
     * 将java对象转换成map，默认包括get函数和属性
     *  某些类无法正常返回，需要自己处理
     * @param obj java对象
     * @param attr 获取属性值
     * @param get 获取get函数
     * @return map对象
     */
    public Map<String, Object> objectToMap(Object obj, boolean attr, boolean get){
        Map<String, Object> map = new HashMap<>();
        try {
            Class<?> clazz = obj.getClass();
            if (attr) {
                for (Field field : clazz.getDeclaredFields()) {
                    field.setAccessible(true);
                    map.put(field.getName(), field.get(obj));
                }
            }
            if (get) {
                for (Method method : clazz.getDeclaredMethods()) {
                    if (method.getName().startsWith("get")) {
                        map.put(method.getName(), method.invoke(obj));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return map;
    }

    protected int getDataType(String callMethod) {
        int type = DATA_BOOL;
        switch (callMethod) {
            case "bFunc":
                type = DATA_BOOL;
                break;

            case "sFunc":
                type = DATA_STRING;
                break;

            case "dFunc":
                type = DATA_MAP;
                break;

            default:
        }

        return type;
    }

    protected void onCheckPermission(String permissionType) {
        switch (permissionType) {
            case Const.P_bluetooth:
                PermissionHelper.getInstance().checkBluetoothPermissions(Tools.getActivity(), success -> {
                    permissionCallback(success, Const.P_bluetooth);
                });
                break;
            case Const.P_location:
                PermissionHelper.getInstance().checkLocationPermissions(Tools.getActivity(), success -> {
                    permissionCallback(success, Const.P_location);
                });
                break;
            case Const.P_contact:
                PermissionHelper.getInstance().checkContactPermissions(Tools.getActivity(), success -> {
                    permissionCallback(success, Const.P_contact);
                });
                break;
            case Const.P_call:
                PermissionHelper.getInstance().checkCallPermissions(Tools.getActivity(), success -> {
                    permissionCallback(success, Const.P_call);
                });
                break;
            case Const.P_sms:
                PermissionHelper.getInstance().checkSmsPermissions(Tools.getActivity(), success -> {
                    permissionCallback(success, Const.P_sms);
                });
                break;

            case Const.P_storage:
                PermissionHelper.getInstance().checkStoragePermission(Tools.getActivity(), success -> {
                    permissionCallback(success, Const.P_sms);
                });
                break;

            case Manifest.permission.PACKAGE_USAGE_STATS:
                if (!PermissionHelper.hasAppOpsPermission()) {
                    PermissionHelper.toAppOpsPage();
                }
                break;

            default:
                PermissionHelper.getInstance().checkPermission(Tools.getActivity(), permissionType, success -> {
                    permissionCallback(success, permissionType);
                });
        }
    }

    private boolean hasPermission(String permission) {
        boolean result = false;
        switch (permission) {
            case Const.P_bluetooth:
                result = PermissionHelper.hasBluetoothPermissions();
                break;
            case Const.P_location:
                result = PermissionHelper.hasLocationPermissions();
                break;
            case Const.P_contact:
                result = PermissionHelper.hasContactPermissions();
                break;
            case Const.P_call:
                result = PermissionHelper.hasCallPermissions();
                break;
            case Const.P_sms:
                result = PermissionHelper.hasSmsPermissions();
                break;

            case Const.P_storage:
                result = PermissionHelper.hasStoragePermissions();
                break;

            case Manifest.permission.PACKAGE_USAGE_STATS:
                result = PermissionHelper.hasAppOpsPermission();
                break;

            default:
                result = PermissionHelper.hasPermission(permission);
        }

        return result;
    }

    private Map<String, Boolean> onQueryPermissions(List<String> permissions) {
        Map<String, Boolean> map = new HashMap<>();

        for (String permission:permissions) {
            if (permission.equals(Const.KEY_NOTIFICATION)) {
                map.put(permission, Tools.isNotificationListenerEnabled(Tools.getApplicationContext()));
            }
            else if (permission.equals(Const.KEY_APP_NOTIFICATION)) {
                map.put(permission, Tools.isNotificationEnabled(Tools.getApplicationContext()));
            }
            else {
                map.put(permission, PermissionHelper.hasPermission(permission));
            }
        }

        return map;
    }

    /**
     * 权限检测返回通知
     * @param success success
     * @param type type
     */
    protected void permissionCallback(boolean success, String type) {
        Map<String, Object> map = new HashMap<>();
        map.put(Const.KEY_PATH, getPath());
        map.put(Const.KEY_METHOD, Const.M_checkPermissions);
        map.put(Const.KEY_RESULT, success);
        map.put(Const.KEY_VALUE, type);
        eventHandler.publishEvent(map);
    }


    private TimeDisplay getTimeDisplay(MethodCall call) {
        TimeDisplay timeDisplay = new TimeDisplay();
        if (call.hasArgument(Const.TIME_FORMAT)) {
            int timeFormat = call.argument(Const.TIME_FORMAT);
            timeDisplay.setTimeFormat(timeFormat);
        }
        if (call.hasArgument(Const.DATE_FORMAT)) {
            int dateFormat = call.argument(Const.DATE_FORMAT);
            timeDisplay.setTimeFormat(dateFormat);
        }

        return timeDisplay;
    }

    protected TimeClock getTimeClock() {
        TimeClock timeClock = new TimeClock();
        timeClock.setTimeSeconds((int) (System.currentTimeMillis()/1000));
//        String displayName = TimeZone.getDefault().getDisplayName();
        timeClock.setTimeZone(8);
        return timeClock;
    }

    protected TimeDisplay getTimeDisplay() {
        TimeDisplay display = new TimeDisplay();

        if (DateFormat.is24HourFormat(Tools.getApplicationContext())) {
            display.setTimeFormat(TimeDisplay.TIME_HOUR_24);
        }
        else {
            display.setTimeFormat(TimeDisplay.TIME_HOUR_12);
        }

        return display;
    }

    public void release() {
        handler.removeCallbacksAndMessages(null);
    }

    protected void i(Object msg) {
        Log.i(getPath(), msg.toString());
    }

    protected void d(Object msg) {
        Logger.d(getPath(), msg.toString());
    }

    abstract public String getPath();
}
